package com.whz.generic.json;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 基于Jackson的字符串序列化工具,做了基础的配置
 *
 * @author whz
 */
@UtilityClass
public class JacksonUtils {

    public static final ObjectMapper MAPPER = createDefaultObjectMapper();

    public static ObjectMapper createDefaultObjectMapper() {
        final ObjectMapper objectMapper = new ObjectMapper();

        // 序列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS);

        // 反序列化
        objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        // json parser
        objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
        objectMapper.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true);

        // jdk8 特性
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.registerModule(new Jdk8Module());

        return objectMapper;
    }

    public static ObjectMapper createDefaultObjectMapper(Collection<SimpleModule> modules) {
        ObjectMapper mapper = createDefaultObjectMapper();
        if (!CollectionUtils.isEmpty(modules)) {
            mapper.registerModules(modules);
        }
        return mapper;
    }

    @SneakyThrows
    public String toJsonString(Object obj) {
        return MAPPER.writeValueAsString(obj);
    }

    @SneakyThrows
    public <T> T parseObject(String json, Class<T> clazz) {
        return MAPPER.readValue(json, clazz);
    }

    /**
     * jackson最快的序列化方式,该方法会flush和close流
     *
     * @param outputStream POJO序列化到output
     * @param obj          POJO对象
     */
    public void writeToOutput(OutputStream outputStream, Object obj) {
        try {
            MAPPER.writeValue(outputStream, obj);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 直接从流中反序列化POJO对象
     *
     * @param inputStream 输入流
     * @param clz         指定POJO类型
     * @param <T>         任意类型
     * @return POJO对象
     */
    @SneakyThrows
    public <T> T parseObject(InputStream inputStream, Class<T> clz) {
        return MAPPER.readValue(inputStream, clz);
    }

    /**
     * 最快的Jackson反序列化方式
     *
     * @param input 从流中来的字节数字
     * @param clz   POJO类型
     * @param <T>   任意类型
     * @return POJO对象
     */
    @SneakyThrows
    public <T> T parseObject(byte[] input, Class<T> clz) {
        return MAPPER.readValue(input, clz);
    }

    @SneakyThrows
    public <T> List<T> parseList(String json, TypeReference<List<T>> typeReference) {
        return MAPPER.readValue(json, typeReference);
    }

    @SneakyThrows
    public <K, V> Map<K, V> parseMap(String json, TypeReference<Map<K, V>> typeReference) {
        return MAPPER.readValue(json, typeReference);
    }


    /**
     * 制定复杂的参数类型 ,如List<User>,List<Set<User>>,Map<String,User>
     *
     * @param parametrized     泛型集合          f
     * @param parameterClasses 参数类型
     * @return jackson 可识别类型            j
     */
    public static JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses) {
        return MAPPER.getTypeFactory().constructParametricType(parametrized, parameterClasses);
    }
}